/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "PCMConvert.h"

namespace liba
{
namespace noise
{

template<int bytes>
struct Getter
{
};

// 0x80 means silence in 8bit formats
template<>
struct Getter<1>
{
	static int get_value(const unsigned char * src)
	{
		return ((*src - 0x80) << 24);
	}
	static void put_value(unsigned char * dst, int val)
	{
		*dst = (val >> 24) + 0x80;
	}
};

template<>
struct Getter<2>
{
	static int get_value(const unsigned char * src)
	{
#ifdef MAC_VERSION
		return (*src << 24) + (*(src+1) << 16);
#else
		return (*src << 16) + (*(src+1) << 24);
#endif
	}
	static void put_value(unsigned char * dst, int val)
	{
#ifdef MAC_VERSION
		*dst = (val >> 24);
		*(dst+1) = (val >> 16);
#else
		*dst = (val >> 16);
		*(dst+1) = (val >> 24);
#endif
	}
};

template<>
struct Getter<3>
{
	static int get_value(const unsigned char * src)
	{
#ifdef MAC_VERSION
		return (*src << 24) + (*(src+1) << 16) + (*(src+2) << 8);
#else
		return (*src << 8) + (*(src+1) << 16) + (*(src+2) << 24);
#endif
	}
	static void put_value(unsigned char * dst, int val)
	{
#ifdef MAC_VERSION
		*dst = (val >> 24);
		*(dst+1) = (val >> 16);
		*(dst+2) = (val >> 8);
#else
		*dst = (val >> 8);
		*(dst+1) = (val >> 16);
		*(dst+2) = (val >> 24);
#endif
	}
};

template<>
struct Getter<4>
{
	static int get_value(const unsigned char * src)
	{
#ifdef MAC_VERSION
		return (*src <<  24) + (*(src+1) << 16) + (*(src+2) << 8) + (*(src+3));
#else
		return (*src) + (*(src+1) << 8) + (*(src+2) << 16) + (*(src+3) << 24);
#endif
	}
	static void put_value(unsigned char * dst, int val)
	{
#ifdef MAC_VERSION
		*dst = (val >> 24);
		*(dst+1) = (val >> 16);
		*(dst+2) = (val >> 8);
		*(dst+3) = (val);
#else
		*dst = (val);
		*(dst+1) = (val >> 8);
		*(dst+2) = (val >> 16);
		*(dst+3) = (val >> 24);
#endif
	}
};

typedef void (*convert_fun)(const unsigned char * src, int src_stride, 
						 unsigned char * dst, int dst_stride, int mask, int samples);

template<int src_bytes, int dst_bytes>
struct Convert
{
	static void convert(const unsigned char * src, int src_stride, 
						 unsigned char * dst, int dst_stride, int mask, int samples)
	{
		do
		{
			int value = Getter<src_bytes>::get_value(src);
			value &= mask;
			Getter<dst_bytes>::put_value(dst, value );
			src += src_stride;
			dst += dst_stride;
		}while( --samples != 0 );
	}
};


static convert_fun convert_table[4][4] =
{
	{ Convert<1,1>::convert, Convert<1,2>::convert, Convert<1,3>::convert, Convert<1,4>::convert },
	{ Convert<2,1>::convert, Convert<2,2>::convert, Convert<2,3>::convert, Convert<2,4>::convert },
	{ Convert<3,1>::convert, Convert<3,2>::convert, Convert<3,3>::convert, Convert<3,4>::convert },
	{ Convert<4,1>::convert, Convert<4,2>::convert, Convert<4,3>::convert, Convert<4,4>::convert }
};

void pcm_bit_convert(const void * src, int src_stride, int src_bits, int src_bytes,
						 void * dst, int dst_stride, int dst_bits, int dst_bytes, int samples)
{
	convert_fun fun = convert_table[src_bytes-1][dst_bytes-1];
	int src_mask = 0xffffffff << (32 - src_bits);
	int dst_mask = 0xffffffff << (32 - dst_bits);
	fun( reinterpret_cast<const unsigned char *>( src ), src_stride, reinterpret_cast<unsigned char *>( dst ), dst_stride, src_mask & dst_mask, samples );
}

void pcm_write_silence(void * dst, int dst_stride, int dst_bits, int dst_bytes, int samples)
{
	unsigned char * char_dst = reinterpret_cast<unsigned char *>( dst );
	for( int i = 0; i != samples; ++i )
	{
		for( int k = 0; k != dst_bytes; ++k )
			char_dst[k] = 0;
		char_dst += dst_stride;
	}
}

} // namespace noise
} // namesapce liba

